﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DTGroup.Networking.Framework.Utils;
using DTGroup.iNek.Business;
using System.IO;
using DTGroup.iNek.Model;
using DTGroup.iNek.Utility;
using System.Net;
using System.Diagnostics;
using System.Threading.Tasks;
using DTGroup.iNek.Configuration;

namespace DTGroup.iNek.WinUI.FingerPrintDatabase
{
    public partial class SyntheticFingerprintLibrary : Form
    {
        private NLog.Logger logger = new NLog.LogFactory().GetCurrentClassLogger();


        public SyntheticFingerprintLibrary()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 选择工参文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog fileDialog = new OpenFileDialog())
            {
                fileDialog.AutoUpgradeEnabled = false;

                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    string filePath = fileDialog.FileName;
                    this.textBox2.Text = filePath;
                }
            }
        }

        /// <summary>
        /// 选择winprop输出路径
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog folder = new FolderBrowserDialog())
            {
                if (folder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    textBox3.Text = folder.SelectedPath;
                }
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {

            SyntheticPar item = new SyntheticPar();
            item.City = textBox1.Text;
            item.CellPath = textBox2.Text;
            item.WinpropOutPut = textBox3.Text;
            item.FPDPath = textBox4.Text;

            backgroundWorker1.RunWorkerAsync(item);

        }



        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            //1、判断小区类型，过滤室内小区
            //2、根据工参里最大最小经纬度，划定范围
            //3、根据1000米划分片
            //4、确定每个分片经纬度范围
            //5、根据分片范围外扩500米，查询小区
            //6、加载分片范围内小区数据到一个集合
            //7、过滤范围外的栅格
            //8、过滤高层室外栅格
            //9、根据栅格ID分组
            //10、按RSRP排序，取前11条
            //11、最大值为主服务小区
            //12、过滤主服务小区小于-120的分组
            //12、写文件
            MessageBox.Show("开始合成");
            int sliceLength = ConfigurationManger.Instance.FingerprintDatabase_SettingsExt.SliceLength; 
            int MaxDegreeOfParallelism = ConfigurationManger.Instance.FingerprintDatabase_SettingsExt.MaxDegreeOfParallelism; 

            SyntheticPar item = (SyntheticPar)e.Argument;

            string city = item.City;
            string cellPath = item.CellPath;
            string winpropOutPut = item.WinpropOutPut;
            string fpdPath = item.FPDPath;

            int FileInfoCount = ConfigurationManger.Instance.FingerprintDatabase_SettingsExt.FileInfoCount; //2000000

            string fileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + new Random().Next(10000);
            string fpdFilePath = fpdPath + "\\" + fileName + ".csv";

            File.Create(fpdFilePath);

            string pamr = sliceLength + "&" + FileInfoCount + "&" + city + "&" + cellPath + "&" + winpropOutPut + "&" + fpdPath + "&" + fpdFilePath + "&";

            DataTable tabCell = DTGroup.Networking.Framework.Utils.ExcelUtil.ExcelToDataTable(cellPath, "站点信息", true);

            #region 1、判断小区类型，过滤室内小区

            List<Res_Cell> allCellNew = new List<Res_Cell>();

            foreach (DataRow dr in tabCell.Rows)
            {
                if (dr["基站类型"].ToString().Equals("室外"))
                {
                    Res_Cell cell = new Res_Cell();

                    double mx = 0, my = 0;

                    cell.CellName = dr["小区名称"].ToString();
                    cell.HorizonAngleToEast = (int)Convert.ToDouble(dr["扇区方位角"]);
                    cell.Longitude = Convert.ToDouble(dr["小区经度"]);
                    cell.Latitude = Convert.ToDouble(dr["小区纬度"]);
                    cell.ObjectID = Convert.ToInt32(dr["ObjectId"]);

                    loc_to_mercator(cell.Longitude, cell.Latitude, ref mx, ref my);

                    cell.CellLongitude = mx;
                    cell.CellLatitude = my;

                    allCellNew.Add(cell);
                }
            }

            tabCell.Dispose();
            tabCell = null;

            string indoorGridFile = System.AppDomain.CurrentDomain.BaseDirectory + "\\" + city + ".txt";

            #endregion

            #region 2、根据工参里最大最小经纬度，划定范围
            double maxX = allCellNew.Max(o => o.CellLongitude);
            double maxY = allCellNew.Max(o => o.CellLatitude);
            double minX = allCellNew.Min(o => o.CellLongitude);
            double minY = allCellNew.Min(o => o.CellLatitude);
            #endregion

            #region 3、根据1000米划分片 4、确定每个分片经纬度范围 5、根据分片范围外扩500米，查询小区

            List<string> keys = new List<string>();
            for (double i = minX; i < maxX + sliceLength; i = i + sliceLength)
            {
                for (double j = minY; j < maxY + sliceLength; j = j + sliceLength)
                {
                    List<Res_Cell> lsCell = allCellNew.Where(o => o.CellLongitude >= i - 500 && o.CellLongitude <= i + sliceLength + 500 && o.CellLatitude >= j - 500 && o.CellLatitude <= j + sliceLength + 500).ToList();
                    if (lsCell.Count > 0)
                    {
                        keys.Add(pamr + i + "_" + j);
                    }
                }
            }

            allCellNew.Clear();
            allCellNew = null;
            #endregion
            Parallel.ForEach(keys, new ParallelOptions() { MaxDegreeOfParallelism = MaxDegreeOfParallelism }, (string key) =>
            {
                logger.Info(key);
                Process process = Process.Start(System.AppDomain.CurrentDomain.BaseDirectory + "\\ConsoleApplication1.exe", key);
                process.WaitForExit();
            });
            //MessageBox.Show(keys.First());
            MessageBox.Show("合成成功！");

        }

        public void writeBATFile(string fpdPath)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("@echo off");
            sb.AppendLine("set ftpUser=jc_rc_ftp");
            sb.AppendLine("set ftpPass=Rc@12345");
            sb.AppendLine("set ftpIP=10.78.142.179");
            sb.AppendLine("set ftpPort=22021");

            sb.AppendLine("set ftpFolder=/jc_rc/rc_hive_db/fpdTest/");

            sb.AppendLine("set LocalFolder= " + fpdPath);

            sb.AppendLine("set ftpFile=%~dp0tempftp.txt");
            sb.AppendLine(">\"%ftpFile%\" (");
            sb.AppendLine("echo open %ftpIP% %ftpPort%");
            sb.AppendLine("echo,%ftpUser%");
            sb.AppendLine("echo,%ftpPass%");
            sb.AppendLine("echo mkdir /jc_rc/rc_hive_db/fpdTest/");
            sb.AppendLine("echo cd %ftpFolder%");
            sb.AppendLine("echo lcd %LocalFolder%");
            sb.AppendLine("echo bin");
            sb.AppendLine("echo mput **");
            sb.AppendLine("echo bye");
            sb.AppendLine(")");

            sb.AppendLine("echo %ftpFile%");
            sb.AppendLine("\"C:\\Windows\\System32\\ftp.exe\" -v -i -s:\"%ftpFile%\" ");

            string filePath = System.AppDomain.CurrentDomain.BaseDirectory + "\\testChange.bat";
            if (!File.Exists(filePath))
            {
                FileStream fs1 = new FileStream(filePath, FileMode.Create, FileAccess.Write);//创建写入文件
                StreamWriter sw = new StreamWriter(fs1);
                sw.WriteLine(sb);//开始写入值
                sw.Close();
                fs1.Close();
            }
            else
            {
                FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Write);
                StreamWriter sr = new StreamWriter(fs);
                sr.WriteLine(sb);//开始写入值
                sr.Close();
                fs.Close();
            }

            executeBat(System.AppDomain.CurrentDomain.BaseDirectory);

            MessageBox.Show("上传完成");
        }

        public void loc_to_mercator(double lon, double lat, ref double miX, ref double minY)
        {
            double x = lon;
            double y = Math.Log(Math.Tan((lat / 180 * Math.PI + Math.PI / 2) / 2)) * 180 / Math.PI;
            miX = x * 20037508.3427892 / 180;
            minY = y * 20037508.3427892 / 180;
        }

        public void executeBat(string nodePath)
        {
            Process proc = null;
            try
            {
                string targetDir = string.Format(nodePath);//this is where testChange.bat lies
                proc = new Process();
                proc.StartInfo.WorkingDirectory = targetDir;
                proc.StartInfo.FileName = "testChange.bat";
                //proc.StartInfo.Arguments = string.Format("10");//this is argument
                //proc.StartInfo.CreateNoWindow = true;
                proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;//这里设置DOS窗口不显示，经实践可行
                proc.Start();
                proc.WaitForExit();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception Occurred :{0},{1}", ex.Message, ex.StackTrace.ToString());
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //textBox5.Text += e.UserState.ToString() + "\r\n";
            label5.Text = e.UserState.ToString();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show("OK");
        }

        private void button4_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog folder = new FolderBrowserDialog())
            {
                if (folder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    textBox4.Text = folder.SelectedPath;
                }
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            writeBATFile(textBox4.Text);
        }

        #region Progrm codes
        void FilesCombination(int sliceLength, int FileInfoCount, string city, string cellPath, string winpropOutPut, string fpdPath, string fpdFilePath, string ke)
        {
            string p_day = DateTime.Now.ToString("yyyyMMddHH");

            DataTable tabCell = DTGroup.Networking.Framework.Utils.ExcelUtil.ExcelToDataTable(cellPath, "站点信息", true);

            //1、判断小区类型，过滤室内小区
            //2、根据工参里最大最小经纬度，划定范围
            //3、根据1000米划分片
            //4、确定每个分片经纬度范围
            //5、根据分片范围外扩500米，查询小区
            //6、加载分片范围内小区数据到一个集合
            //7、过滤范围外的栅格
            //8、过滤高层室外栅格
            //9、根据栅格ID分组
            //10、按RSRP排序，取前11条
            //11、最大值为主服务小区
            //12、过滤主服务小区小于-120的分组
            //12、写文件

            #region 1、判断小区类型，过滤室内小区

            List<Res_Cell> allCell = new List<Res_Cell>();

            foreach (DataRow dr in tabCell.Rows)
            {
                if (dr["基站类型"].ToString().Equals("室外"))
                {
                    Res_Cell cell = new Res_Cell();

                    double mx = 0, my = 0;

                    cell.CellName = dr["小区名称"].ToString();
                    cell.HorizonAngleToEast = (int)Convert.ToDouble(dr["扇区方位角"]);
                    cell.Longitude = Convert.ToDouble(dr["小区经度"]);
                    cell.Latitude = Convert.ToDouble(dr["小区纬度"]);
                    cell.ObjectID = Convert.ToInt32(dr["ObjectId"]);

                    loc_to_mercator(cell.Longitude, cell.Latitude, ref mx, ref my);

                    cell.CellLongitude = mx;
                    cell.CellLatitude = my;

                    allCell.Add(cell);
                }
            }

            tabCell.Dispose();
            tabCell = null;

            string indoorGridFile = System.AppDomain.CurrentDomain.BaseDirectory + city + ".txt";
            HashSet<string> lsIndoorGrid = new HashSet<string>();

            List<Res_Cell> allCellNew = new List<Res_Cell>();

            /* useless
            using (StreamReader sr = new StreamReader(indoorGridFile))
            {
                string line = sr.ReadLine();

                do
                {
                    lsIndoorGrid.Add(line);

                    //double x = Convert.ToDouble(line.Split('_')[0]) * 5;
                    //double y = Convert.ToDouble(line.Split('_')[1]) * 5;

                    //foreach (var o in allCell)
                    //{
                    //    if ((!allCellNew.Contains(o)) && ((o.CellLongitude - 500) <= x && (o.CellLongitude + 500) >= x && (o.CellLatitude - 500) <= y && (o.CellLatitude + 500) >= y))
                    //    {
                    //        allCellNew.Add(o);
                    //    }
                    //}

                    line = sr.ReadLine();
                } while (!string.IsNullOrEmpty(line));
            }
            */

            #endregion

            #region 3、根据1000米划分片 4、确定每个分片经纬度范围 5、根据分片范围外扩500米，查询小区

            Dictionary<string, List<Res_Cell>> dicLsCell = new Dictionary<string, List<Res_Cell>>();

            double sliceMinX = Convert.ToDouble(ke.Split('_')[0]);
            double sliceMinY = Convert.ToDouble(ke.Split('_')[1]);
            double sliceMaxX = sliceMinX + sliceLength;
            double sliceMaxY = sliceMinY + sliceLength;

            List<Res_Cell> lsCell = allCell.Where(o => o.CellLongitude >= sliceMinX - 500 && o.CellLongitude <= sliceMaxX + 500 && o.CellLatitude >= sliceMinY - 500 && o.CellLatitude <= sliceMaxY + 500).ToList();
            allCell.Clear();
            allCell = null;

            #endregion

            int fileInfoNum = 0;
            int fileInfoCount = FileInfoCount;// 1000000;

            FileStream fs = new System.IO.FileStream(fpdFilePath, FileMode.Append, FileAccess.ReadWrite, FileShare.ReadWrite);
            StreamWriter sw = new StreamWriter(fs);

            string[] nodeFolder = Directory.GetDirectories(winpropOutPut);

            foreach (string nodePath in nodeFolder)
            {
                List<Fpd_s_join_n_over0innerdoor> lsFPD = new List<Fpd_s_join_n_over0innerdoor>();
                int floorNum = Convert.ToInt32(GetName(nodePath));
                foreach (Res_Cell cell in lsCell)
                {
                    string filePath = nodePath + "\\" + cell.CellName + "_OutdoorOutput.txt";
                    //Console.WriteLine("filePath:" + filePath);
                    if (File.Exists(filePath))
                    {
                        #region 6、加载分片范围内小区数据到一个集合 7、过滤范围外的栅格

                        using (StreamReader sr = new StreamReader(filePath))
                        {
                            string line = sr.ReadLine();
                            do
                            {
                                string[] lins = line.Split('\t');
                                double mx = Convert.ToDouble(lins[0]);
                                double my = Convert.ToDouble(lins[1]);
                                string grid = (int)mx / 5 + "_" + (int)my / 5;

                                if (mx > sliceMinX && mx < sliceMaxX && my > sliceMinY && my < sliceMaxY)
                                {
                                    if ((floorNum > 0 && lsIndoorGrid.Contains(grid)) || (floorNum == 0))
                                    {
                                        string rsrp = lins[2];
                                        double lon = 0, lat = 0;

                                        mercator_to_loc(mx, my, ref lon, ref lat);

                                        int calibrateGridId = ComputeCalibrateGridID(
                                            GeometryUtil.ConvertToMeracatol(new GeometryPoint(cell.Longitude, cell.Latitude)),
                                            GeometryUtil.ConvertToMeracatol(new GeometryPoint(lon, lat)),
                                            cell.HorizonAngleToEast,
                                            100,
                                            50,
                                            10);

                                        Fpd_s_join_n_over0innerdoor fpdItem = new Fpd_s_join_n_over0innerdoor();
                                        fpdItem.p_day = p_day;
                                        fpdItem.p_city = city;
                                        fpdItem.gridid = grid;
                                        fpdItem.longitude = lon.ToString();
                                        fpdItem.latitude = lat.ToString();
                                        fpdItem.gridx = ((int)mx / 5).ToString();
                                        fpdItem.gridy = ((int)my / 5).ToString();
                                        fpdItem.height = (floorNum * 3).ToString();
                                        fpdItem.objectid = cell.ObjectID.ToString();
                                        fpdItem.rsrp = rsrp;
                                        fpdItem.calibrategridid = calibrateGridId.ToString();

                                        lsFPD.Add(fpdItem);
                                    }
                                }

                                line = sr.ReadLine();
                            } while (!string.IsNullOrEmpty(line));
                        }

                        #endregion
                    }
                }
                //Console.WriteLine("lsFPD.Count:" + lsFPD.Count);
                #region 9、根据栅格ID分组 10、按RSRP排序，取前11条 11、最大值为主服务小区 12、过滤主服务小区小于-120的分组 12、写文件

                foreach (var g in lsFPD.GroupBy(x => x.gridid))
                {
                    if (g.Count() > 1)
                    {
                        List<Fpd_s_join_n_over0innerdoor> groupFpd = g.ToList().OrderByDescending(o => Convert.ToDouble(o.rsrp)).ToList();

                        Fpd_s_join_n_over0innerdoor serverFpd = groupFpd[0];
                        double rsrp = Convert.ToDouble(serverFpd.rsrp);

                        if (rsrp > -120)
                        {
                            int endi = 11;
                            if (groupFpd.Count < 11) { endi = groupFpd.Count; }

                            for (int i = 1; i < endi; i++)
                            {
                                //if (fileInfoNum == fileInfoCount || fileInfoNum == 0)
                                //{
                                //    fileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + new Random().Next(10000);
                                //    fpdFilePath = fpdPath + "\\" + fileName + ".csv";

                                //    if (fs != null) fs.Flush();
                                //    if (sw != null) sw.Flush();
                                //    if (sw != null) sw.Dispose();
                                //    if (fs != null) fs.Dispose();

                                //    fs = new System.IO.FileStream(fpdFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                                //    sw = new StreamWriter(fs);

                                //    fileInfoNum = 0;
                                //}

                                sw.WriteLine(string.Concat(serverFpd.gridid, ",", serverFpd.longitude, ",", serverFpd.latitude, ",", serverFpd.gridx, ",", serverFpd.gridy, ",", serverFpd.height, ",", serverFpd.objectid, ",", serverFpd.rsrp, ",", serverFpd.calibrategridid, ",", groupFpd[i].objectid, ",", groupFpd[i].rsrp, ",", groupFpd[i].calibrategridid, ",", serverFpd.p_city, ",", serverFpd.p_day));

                                //fileInfoNum++;
                            }
                        }
                        for (int a = groupFpd.Count - 1; a >= 0; a--)
                        {
                            groupFpd[a] = null;
                        }

                        groupFpd.Clear();
                        groupFpd.Capacity = 0;
                        groupFpd = null;
                    }
                }

                for (int a = lsFPD.Count - 1; a >= 0; a--)
                {
                    lsFPD[a] = null;
                }
                lsFPD.Clear();
                lsFPD.Capacity = 0;
                lsFPD = null;

                #endregion
            }

            for (int a = lsCell.Count - 1; a >= 0; a--)
            {
                lsCell[a] = null;
            }

            lsCell.Clear();
            lsCell.Capacity = 0;
            lsCell = null;

            dicLsCell.Clear();
            GC.Collect();

            if (fs != null) fs.Flush();
            if (sw != null) sw.Flush();
            if (sw != null) sw.Dispose();
            if (fs != null) fs.Dispose();
        }


        /// <summary>
        /// 计算 当前栅格的校准栅格ID
        /// </summary>
        /// <param name="cellPoint">小区点</param>
        /// <param name="gridPoint">栅格点</param>
        /// <param name="cellHorizonAngleToEast">小区水平方向角</param>
        /// <param name="angleNum">角度的栅格个数</param>
        /// <param name="distainceGridNum">距离的栅格个数</param>
        /// <param name="deltD">距离的步长</param>
        /// <returns>校准后的栅格ID</returns>
        public int ComputeCalibrateGridID(DTGroup.iNek.Utility.GeometryPoint cellPoint, DTGroup.iNek.Utility.GeometryPoint gridPoint, int cellHorizonAngleToEast, int angleNum, int distainceGridNum, int deltD)
        {
            // b) 计算 栅格坐标 与小区方向的夹角  

            /* 计算栅格坐标和小区方向的夹角 
             *   1、弧度 = arctan(y1-y1/x1-x1) )                           值在-180—180之间
             *   2、angleHoritonToEast=[30-(angleHoritonToEast-90)]%360
             *   3、角度1 = 角度0-angleHoritonToEast                        值在 -540—180之间
             *   4、角度2=（角度1+720）%360
            */

            // 1、弧度 = arctan(y1-y1/x1-x1) )    值在-180—180之间
            double radian = Math.Atan2((gridPoint.STY - cellPoint.STY), (gridPoint.STX - cellPoint.STX));
            // 将 弧度转换为角度 弧度转角度= 180/PI*弧度
            int angle0 = Convert.ToInt32(180 / Math.PI * radian);
            // 2、angleHoritonToEast=[30-(angleHoritonToEast-90)]%360
            int angleHoritonToEast = (360 - (cellHorizonAngleToEast - 90)) % 360;
            // 3、角度1 = 角度0-angleHoritonToEast 
            int angle1 = angle0 - angleHoritonToEast;
            // 4、角度2=（角度1+720）%360
            int angle2 = (angle1 + 720) % 360;

            // c) 计算夹角的索引

            /*
             * 1、角度3=（角度2+180/a）%360;                             0-360
             * 2、tempA = Math.Cell(角度3*a/360)                         0-19
             */

            // 1、角度3=（角度2+180/a）%360;  
            int angle3 = (angle2 + 180 / angleNum) % 360;
            // 2、计算角度索引 tempA = Math.Cell(角度3*a/360)     
            int angleIndex = Convert.ToInt32(Math.Floor(angle3 * angleNum / 360d));

            // 小区和栅格的中心点的距离
            double d = DTGroup.iNek.Utility.GeometryUtil.GetDistanceOfTwoPoints(gridPoint, cellPoint);
            // d) 计算距离索引
            // b = min(Math.Celling())
            double tempB = Math.Min(Math.Floor(d / deltD), distainceGridNum - 1);

            // e) 计算校准的栅格编号
            return Convert.ToInt32(angleIndex * distainceGridNum + tempB);
        }

        private string GetName(string str)
        {
            if (string.IsNullOrEmpty(str)) return "";

            if (str.Length > 0)
            {
                if (str.LastIndexOf("/") > 0)
                {
                    str = str.Substring(str.LastIndexOf("/") + 1);
                }
                else if (str.LastIndexOf("\\") > 0)
                {
                    str = str.Substring(str.LastIndexOf("\\") + 1);
                }
            }
            return str;
        }

        public void mercator_to_loc(double mercator_x, double mercator_y, ref double lon, ref double lat)
        {
            double x = mercator_x / 20037508.3427892 * 180;
            double y = mercator_y / 20037508.3427892 * 180;
            lon = x;
            lat = 180 / Math.PI * (2 * Math.Atan(Math.Exp(y * Math.PI / 180)) - Math.PI / 2);
        }

        #endregion
    }







    public class SyntheticPar
    {
        public string City { get; set; }
        public string CellPath { get; set; }
        public string WinpropOutPut { get; set; }
        public string FPDPath { get; set; }
    }
    public class Fpd_s_join_n_over0innerdoor
    {
        public string p_day { get; set; }
        public string p_city { get; set; }
        public string gridid { get; set; }
        public string longitude { get; set; }
        public string latitude { get; set; }
        public string gridx { get; set; }
        public string gridy { get; set; }
        public string height { get; set; }
        public string objectid { get; set; }
        public string rsrp { get; set; }
        public string calibrategridid { get; set; }
        public string n_objectid { get; set; }
        public string n_rsrp { get; set; }
        public string ncalibrategridid { get; set; }
    }
}
